<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CIO与IOReader类对比分析</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            line-height: 1.6;
            margin: 0;
            padding: 20px;
            color: #333;
        }
        h1 {
            color: #0066cc;
            border-bottom: 2px solid #0066cc;
            padding-bottom: 10px;
        }
        h2 {
            color: #0066cc;
            margin-top: 30px;
            border-left: 4px solid #0066cc;
            padding-left: 10px;
        }
        h3 {
            color: #333;
            margin-top: 20px;
        }
        table {
            border-collapse: collapse;
            width: 100%;
            margin: 20px 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px 12px;
            text-align: left;
        }
        th {
            background-color: #f2f2f2;
            font-weight: bold;
        }
        tr:nth-child(even) {
            background-color: #f9f9f9;
        }
        .code {
            font-family: Consolas, Monaco, 'Andale Mono', monospace;
            background-color: #f5f5f5;
            padding: 15px;
            border-radius: 5px;
            overflow-x: auto;
            font-size: 14px;
            line-height: 1.4;
            border: 1px solid #ddd;
        }
        .highlight {
            background-color: #ffffcc;
            font-weight: bold;
        }
        .note {
            background-color: #e6f7ff;
            border-left: 4px solid #1890ff;
            padding: 10px 15px;
            margin: 15px 0;
        }
    </style>
</head>
<body>
    <h1>CIO与IOReader类对比分析</h1>
    
    <div class="note">
        本文档对比分析了BA项目中的CIO和IOReader两个类的功能、实现和使用场景差异，帮助开发人员理解两个类的设计目的和应用场景。
    </div>

    <h2>1. 类的基本信息对比</h2>
    <table>
        <tr>
            <th>特性</th>
            <th>CIO类</th>
            <th>IOReader类</th>
        </tr>
        <tr>
            <td>继承关系</td>
            <td>继承自QThread</td>
            <td>继承自QObject</td>
        </tr>
        <tr>
            <td>运行模式</td>
            <td>独立线程运行</td>
            <td>非线程类，需要外部调用</td>
        </tr>
        <tr>
            <td>串口对象</td>
            <td>CXnSerial* m_pSerial</td>
            <td>CXnSerial* m_pRtuClient</td>
        </tr>
        <tr>
            <td>主要功能</td>
            <td>读取DI状态、控制DO输出、读取DO状态</td>
            <td>读取DI状态、控制DO输出、读取DO状态、特定控制功能</td>
        </tr>
        <tr>
            <td>通信协议</td>
            <td>Modbus RTU</td>
            <td>Modbus RTU</td>
        </tr>
        <tr>
            <td>数据存储</td>
            <td>使用CIOModule存储IO状态</td>
            <td>使用DryData结构体存储IO状态</td>
        </tr>
    </table>

    <h2>2. 类的构造函数对比</h2>
    <h3>CIO类构造函数：</h3>
    <div class="code">
CIO::CIO(QObject *parent)
	: QThread(parent)
{
	m_pSerial = new CXnSerial;

	//m_pRTDB = 0;
	m_pIOModule = 0;

	m_bSerialOK = false;
#ifdef XNWIN
	m_strPortName = "com4";         // 串口名
#else
	m_strPortName = "/dev/ttyS5";   // 串口名 
	//m_strPortName = "/dev/ttyS8";   // 串口名 
#endif 
	m_iBaudRate = 115200;     // 波特率
	m_iDataBits = 8;          // 数据位
	m_iStopBits = 1;          // 停止位
	m_iParity = 0;            // 校验位	
	m_iRtscts = 0;

#if XNWIN
	m_bPrintMsg = true;
#else
	m_bPrintMsg = true;
#endif

	m_iSerialDataLen = 0;
    m_logCount = 0;
	m_pSerialDataBuf = new char[DATA_BUFFER_LENGHT];  // data buffer

	printf("XnIO Version %d.%d RTDB %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, BAMS_VERSION_MAJOR, BAMS_VERSION_MINOR, BAMS_VERSION_REVISION);
}
    </div>

    <h3>IOReader类构造函数：</h3>
    <div class="code">
IOReader::IOReader(QObject *parent, QString strRtuName)
	: QObject(parent)
{
    m_strRtuName = strRtuName;
    m_pRtuClient = new CXnSerial;// Client
   	m_bRtuOpened = false;
	
	//m_devAddr = KgGlobalData::ms_zz404D_addr;
    m_iDevIndex = 0;
	
	//m_devDoCount = KgGlobalData::ms_devDoCount;
	//m_devDiCount = KgGlobalData::ms_devDiCount;

	//KgGlobalData::ms_dryData.failedCount = 0;
	//KgGlobalData::ms_dryData.readDiCount = 0;
	//KgGlobalData::ms_dryData.IOCtrl = 0;
	//KgGlobalData::ms_dryData.DIVal = 0;
	
	//KgGlobalData::ms_dryData.bReadDiFlag = false;
	//KgGlobalData::ms_dryData.bReadDoFlag = false;
}
    </div>

    <h2>3. 主要方法对比</h2>
    <table>
        <tr>
            <th>功能</th>
            <th>CIO类</th>
            <th>IOReader类</th>
        </tr>
        <tr>
            <td>初始化</td>
            <td>init()</td>
            <td>openSerialPort()</td>
        </tr>
        <tr>
            <td>运行</td>
            <td>run() - 线程主函数</td>
            <td>无线程主函数，通过外部调用各方法</td>
        </tr>
        <tr>
            <td>读取DI状态</td>
            <td>通过run()中循环调用formDatagram(1,...)和analyDatagram(1,...)</td>
            <td>readDevDIState()</td>
        </tr>
        <tr>
            <td>读取DO状态</td>
            <td>通过run()中循环调用formDatagram(3,...)和analyDatagram(3,...)</td>
            <td>readDevDOState()</td>
        </tr>
        <tr>
            <td>控制DO输出</td>
            <td>通过run()中循环调用formDatagram(2,...)和analyDatagram(2,...)</td>
            <td>setDevAllDoState()、setDoStateHZ()</td>
        </tr>
        <tr>
            <td>报文构造</td>
            <td>formDatagram()</td>
            <td>多个专用函数，如formSetMultgram15()</td>
        </tr>
        <tr>
            <td>报文解析</td>
            <td>analyDatagram()</td>
            <td>analyRtuDatagram()、analySetSingleDatagram()、analySetMultDatagram15()</td>
        </tr>
        <tr>
            <td>日志输出</td>
            <td>printSendInfo()、printReceInfo()</td>
            <td>printSendInfo()、printReceInfo()、sendLog()</td>
        </tr>
    </table>

    <h2>4. 通信协议实现对比</h2>
    <h3>4.1 CIO类的Modbus通信实现</h3>
    <p>CIO类使用固定的Modbus功能码进行通信：</p>
    <ul>
        <li>读取DI状态：功能码0x02</li>
        <li>控制DO输出：功能码0x05</li>
        <li>读取DO状态：功能码0x01</li>
    </ul>
    <p>CIO类的通信实现特点：</p>
    <ul>
        <li>使用统一的formDatagram()方法构造不同类型的报文</li>
        <li>使用统一的analyDatagram()方法解析不同类型的报文</li>
        <li>在run()方法中循环执行读取DI、控制DO、读取DO的操作</li>
        <li>固定使用两个Modbus站号（1和2）</li>
    </ul>

    <h3>4.2 IOReader类的Modbus通信实现</h3>
    <p>IOReader类同样使用Modbus功能码进行通信，但实现方式更加灵活：</p>
    <ul>
        <li>读取DI状态：功能码0x02</li>
        <li>读取DO状态：功能码0x01</li>
        <li>控制单个DO：功能码0x05</li>
        <li>控制多个DO：功能码0x0F</li>
    </ul>
    <p>IOReader类的通信实现特点：</p>
    <ul>
        <li>针对不同功能使用专用的方法构造和解析报文</li>
        <li>支持更多的Modbus功能码</li>
        <li>支持通过DryData结构体传递参数和结果</li>
        <li>支持更灵活的设备地址配置</li>
    </ul>

    <h2>5. 数据处理方式对比</h2>
    <h3>5.1 CIO类的数据处理</h3>
    <div class="code">
// CIO类中的数据处理示例（从analyDatagram方法中提取）
if (1 == iType) // Read DI type = 1   0x02
{
    uaByteNum = *(uchar*)(pDataBuf + iIndex);
    ++iIndex;
    uaData = *(uchar*)(pDataBuf + iIndex);
    ++iIndex;

    for (iIndex1 = 0; iIndex1 < 8; ++iIndex1)
    {
        if ((iModbusID - 1) * 8 + iIndex1 < 16)
        {
            if ((uaData & 0x01) > 0)
            {
                m_pIOModule->usDI[(iModbusID -1) * 8 + iIndex1] = 1;
            }
            else
            {
                m_pIOModule->usDI[(iModbusID - 1) * 8 + iIndex1] = 0;
            }

            uaData = uaData >> 1;
        }
    }
}
    </div>

    <h3>5.2 IOReader类的数据处理</h3>
    <div class="code">
// IOReader类中的数据处理示例（从readDevDIState方法中提取）
quint16 usDIState = 0; 
for(int k = 0; k < 16; ++k)
{
    if(KgGlobalData::ms_pIOModule->usDI[k] > 0) // usDI[16]
    {
        usDIState = usDIState | (1 << k);
    }
}
dryData.DIVal = usDIState; // added 2023-11-9 读取10寸屏自带的DI状态
//sendLog(0, QString("==================Read DI state(Frame3):%1.").arg(dryData.DIVal));
QString strLog = QString("Read BMS DI state:%1.").arg(dryData.DIVal);
writeKgLogfile(strLog, true);
    </div>

    <h2>6. 使用场景对比</h2>
    <table>
        <tr>
            <th>使用场景</th>
            <th>CIO类</th>
            <th>IOReader类</th>
        </tr>
        <tr>
            <td>适用设备</td>
            <td>适用于固定配置的IO模块，如10寸屏自带的IO模块</td>
            <td>适用于可配置的IO模块，如外接的串口继电器模块</td>
        </tr>
        <tr>
            <td>运行方式</td>
            <td>作为独立线程持续运行，自动循环读写IO状态</td>
            <td>由外部程序按需调用，更灵活控制IO操作时机</td>
        </tr>
        <tr>
            <td>配置灵活性</td>
            <td>配置相对固定，主要通过代码中的常量定义</td>
            <td>配置更灵活，可通过DryData结构体传递参数</td>
        </tr>
        <tr>
            <td>功能扩展性</td>
            <td>功能相对固定，主要支持基本的IO读写</td>
            <td>功能更丰富，支持更多控制命令和特殊操作</td>
        </tr>
    </table>

    <h2>7. 代码结构对比</h2>
    <h3>7.1 CIO类的代码结构</h3>
    <ul>
        <li>继承自QThread，实现run()方法作为线程主函数</li>
        <li>使用统一的报文构造和解析方法</li>
        <li>在run()方法中循环执行IO操作</li>
        <li>使用CIOModule结构体存储IO状态</li>
    </ul>

    <h3>7.2 IOReader类的代码结构</h3>
    <ul>
        <li>继承自QObject，不包含线程功能</li>
        <li>针对不同功能提供专用方法</li>
        <li>通过外部调用执行IO操作</li>
        <li>使用DryData结构体传递参数和结果</li>
        <li>包含更多辅助方法和数据转换功能</li>
    </ul>

    <h2>8. 总结与建议</h2>
    <div class="note">
        <p><strong>功能差异：</strong>CIO类主要用于10寸屏自带IO模块的操作，设计为独立线程自动循环读写IO状态；IOReader类主要用于外接串口继电器模块的操作，提供更灵活的控制方式和更丰富的功能。</p>
        <p><strong>实现差异：</strong>CIO类使用统一的报文构造和解析方法，代码结构相对简单；IOReader类针对不同功能提供专用方法，代码结构更复杂但功能更丰富。</p>
        <p><strong>使用建议：</strong></p>
        <ul>
            <li>对于10寸屏自带IO模块的操作，建议使用CIO类</li>
            <li>对于外接串口继电器模块的操作，建议使用IOReader类</li>
            <li>如需扩展IO功能，可参考IOReader类的实现方式</li>
        </ul>
    </div>

</body>
</html>